package org.dromara.common.social.maxkey;

import cn.hutool.core.lang.Dict;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.json.utils.JsonUtils;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：负责处理与最大密钥认证相关的请求
 */
/**
 * AuthMaxKeyRequest 类继承自 AuthDefaultRequest 类，用于实现特定的认证请求处理
 * 该类主要负责处理与最大密钥认证相关的请求
 */
public class AuthMaxKeyRequest extends AuthDefaultRequest {

    /**
     * 服务器URL配置项
     * 通过SpringUtils工具类获取配置文件中的服务器URL地址
     * 使用静态常量形式存储，便于全局访问和维护
     */
    public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.maxkey.server-url");

    /**
     * 设定归属域
     */
    public AuthMaxKeyRequest(AuthConfig config) {
        super(config, AuthMaxKeySource.MAXKEY);
    }

    /**
     * 构造函数，用于初始化AuthMaxKeyRequest对象
     * 该构造函数使用给定的配置和认证状态缓存来初始化对象
     *
     * @param config        认证配置对象，包含认证相关的配置信息
     * @param authStateCache 认证状态缓存对象，用于存储和检索认证状态
     */
    public AuthMaxKeyRequest(AuthConfig config, AuthStateCache authStateCache) {
        super(config, AuthMaxKeySource.MAXKEY, authStateCache);
    }

    /**
     * 根据授权回调获取访问令牌
     * 该方法使用授权码模式，通过HTTP POST请求从授权服务器获取访问令牌
     *
     * @param authCallback 授权回调对象，包含授权过程中所需的信息
     * @return 返回包含访问令牌的AuthToken对象
     */
    @Override
    protected AuthToken getAccessToken(AuthCallback authCallback) {
        // 根据授权码进行POST请求，以获取访问令牌的JSON字符串
        String body = doPostAuthorizationCode(authCallback.getCode());
        // 将JSON字符串解析为字典对象，以便后续使用
        Dict object = JsonUtils.parseMap(body);
        // oauth/token 验证异常
        if (object.containsKey("error")) {
            throw new AuthException(object.getStr("error_description"));
        }
        // user 验证异常
        if (object.containsKey("message")) {
            throw new AuthException(object.getStr("message"));
        }
        return AuthToken.builder()
            // 设置访问令牌
            .accessToken(object.getStr("access_token"))
            // 设置刷新令牌
            .refreshToken(object.getStr("refresh_token"))
            // 设置ID令牌
            .idToken(object.getStr("id_token"))
            // 设置令牌类型
            .tokenType(object.getStr("token_type"))
            // 设置作用域
            .scope(object.getStr("scope"))
            .build();
    }

    /**
     * 根据身份验证令牌获取用户信息
     * 此方法通过发送请求给特定的端点来获取用户信息，解析响应的JSON数据，并将其转换为字典格式
     * 该方法重写了父类的相应方法，以实现用户身份验证和信息检索的自定义逻辑
     *
     * @param authToken 身份验证令牌，用于识别和验证用户的身份
     * @return 返回一个包含用户信息的字典对象
     */
    @Override
    protected AuthUser getUserInfo(AuthToken authToken) {
        // 发送请求以获取用户信息的JSON字符串
        String body = doGetUserInfo(authToken);
        // 解析JSON字符串为字典格式
        Dict object = JsonUtils.parseMap(body);
        // oauth/token 验证异常
        if (object.containsKey("error")) {
            throw new AuthException(object.getStr("error_description"));
        }
        // user 验证异常
        if (object.containsKey("message")) {
            throw new AuthException(object.getStr("message"));
        }

        // 使用构建者模式构建AuthUser对象
        return AuthUser.builder()
            // 设置用户唯一标识
            .uuid(object.getStr("userId"))
            // 设置用户名
            .username(object.getStr("username"))
            // 设置用户昵称
            .nickname(object.getStr("displayName"))
            // 设置用户头像URL
            .avatar(object.getStr("avatar_url"))
            // 设置用户博客链接
            .blog(object.getStr("web_url"))
            // 设置用户公司信息
            .company(object.getStr("organization"))
            // 设置用户所在位置
            .location(object.getStr("location"))
            // 设置用户邮箱
            .email(object.getStr("email"))
            // 设置用户个人简介
            .remark(object.getStr("bio"))
            // 设置用户认证令牌
            .token(authToken)
            // 设置用户来源
            .source(source.toString())
            .build();
    }

}
